/**
 * @desc 使用缓存来存储结果，以便后续调用耗时的函数不会再次执行相同的工作。
 * 1. Memoization 应该主要用于加速性能缓慢、成本高或耗时的函数调用
 * 2. Memoization 可加快后续调用的速度，因此最好在您预计在相同情况下对同一函数进行多次调用时使用
 * 3. Memoization 将结果存储在内存中，因此在非常不同的情况下多次调用同一函数时应避免使用
 * @param fn
 * @returns {*}
 */
// 利用 Proxy 的 handle 参数的 apply() 方法
const memoize = fn => new Proxy(fn, {
	// 定义一个 map 的缓存对象。
	cache: new Map(),
	// 利用 apply() 方法来判断 cache 缓存来避免重复调用，直接取值即可！
	apply (target, thisArg, argsList) {
		let cacheKey = argsList.toString();
		// 当缓存 cache 未命中
		if(!this.cache.has(cacheKey))
			// 执行方法后将结果存入 cache 数组中
			this.cache.set(cacheKey, target.apply(thisArg, argsList));
		// 返回缓存中的结果
		return this.cache.get(cacheKey);
	}
});

const fibonacci = n => (n <= 1 ? 1 : fibonacci(n - 1) + fibonacci(n - 2));
const memoizedFibonacci = memoize(fibonacci);

// 一次斐波那契数列递归执行约 50ms，采用记忆化直接读取缓存即可
for (let i = 0; i < 100; i ++){
	fibonacci(30);                      // 约5000ms，每次都执行，执行了 100 次。
}
for (let i = 0; i < 100; i ++){
	memoizedFibonacci(30);              // 约50ms，只执行了一次
}
